Repository navigation
fix(claude): share the per-entry limits parser between OAuth and Web - #4614
Conversation
OAuth decoded the whole `limits` array as one typed value, so a single entry with an unexpected field type dropped every model-scoped weekly row. Parse the array with the Web path's entry-by-entry parser instead and remove the OAuth-only limit types. Co-Authored-By: claude-sonnet-5-5 <noreply@anthropic.com>
The per-entry limits parse re-reads the body with JSONSerialization, which rejects numbers JSONDecoder accepts (e.g. 1e400) and threw for the whole response. Fall back to an empty limits list instead, as before. Fold the cross-path limits tests into ClaudeWebUsageExtraWindowTests using the per-test OAuth defaults fixture, assert the Web primary percent in the sibling-shape case, and drop the separate suite. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
🦞👀 Pull request received. I will update this pull request when review starts. ClawSweeper review completeClawSweeper finished reviewing this revision. The review result is being finalized. |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Codex review: needs real behavior proof before merge. What this changesThis PR makes Claude OAuth reuse the Web parser so unexpected limit-field types do not discard valid sibling quota rows. Example: An OAuth response contains a session entry with percent "11" and a Fable weekly entry with percent 5 and numeric resets_at 1791800000.
Review scores
ProductKind: Bug fix · Worth it: Yes · Fix scope: Complete Merge readiness⛔ Blocked before merge - 1 item remains Keep this PR open: it implements explicitly approved parser hardening with a focused change, and inspection found no actionable code defect. Priority: P2 Before merge
FindingsNone. Tests
Agent review detailsHow this fits togetherClaude’s OAuth and Web fetchers turn provider usage responses into shared quota windows consumed by the app and CLI. flowchart LR
A[OAuth response] --> B[OAuth usage decoder]
C[Web response] --> D[Shared entry parser]
B --> D
D --> E[Scoped weekly mapper]
E --> F[Usage snapshot]
F --> G[App and CLI]
Technical reviewBest possible solution: Use the existing per-entry parser for both sources while preserving quota mapping, row ordering, and ordinary OAuth usage. Do we have a high-confidence way to reproduce the issue? The base decoder and supplied fixture establish the array-wide failure deterministically in source; no tests were run during this read-only review, and no malformed live response is documented. Is this the best way to solve the issue? Reusing the existing Web parser is the smallest complete fix for the demonstrated field-type failures and follows the maintainer’s explicit direction. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning medium; reviewed against 6480977b47f6. Provenance checked
TestingProof path: unit tests only. SecurityNone. EvidenceWhat I checked:
Likely related people:
Review metrics
LabelsLabel changes:
Label justifications:
Rating scale6/6 🦀 challenger crab · 5/6 🦞 diamond lobster · 4/6 🐚 platinum hermit · 3/6 🦐 gold shrimp · 2/6 🦪 silver shellfish · 1/6 🧂 unranked krab. Overall follows the weaker of proof and patch quality; ✨ marks media proof (a screenshot, video, or linked artifact) that directly shows the changed behavior. WorkflowClawSweeper edits this one comment on every review. Comment HistoryReview history (1 earlier review cycle)
Reviewed October 18, 2026, 9:43 AM ET / 13:43 UTC (Revision 2). |
|
Added a "Runtime proof" section to the description: a live OAuth run with this branch's CLI and with the released 0.77.0 gives identical output, including the "Fable only" row. A malformed @clawsweeper re-review |
|
🦞👀 Re-review progress:
|
Refs steipete#4614, steipete#4126 Co-authored-by: Akagilnc <akagilnc@gmail.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4790693616
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] | ||
| usage.limits = json.map(ClaudeWebExtraRateWindowParser.scopedWeeklyLimits) |
There was a problem hiding this comment.
Parse OAuth limits without a whole-body serialization gate
When an OAuth response includes an otherwise ignored field whose JSON number Foundation cannot materialize (for example a future field on a sibling limit with 1e400), JSONDecoder successfully builds the usage response but this whole-body JSONSerialization re-read returns nil, so usage.limits is left empty. Before this change the typed decoder ignored such unknown fields and retained a valid weekly_scoped sibling, whereas the new path silently removes its model row; decode limits through a lossy Decodable entry wrapper instead of making it contingent on serializing the entire response.
Useful? React with 👍 / 👎.
# Conflicts: # CHANGELOG.md
|
Merged in e1325bf, thanks @Akagilnc! Claude's OAuth and Web paths now share one per-entry |
…Pi) (#963) * Follow the active claude-swap slot in Auto Claude usage Auto `usage --provider claude` now answers from the active claude-swap slot when the integration is enabled, instead of whichever browser or CLI login the ordinary source order finds. Explicit --source, --account, --all-accounts and a configured non-auto usage source bypass it. If the adapter fails or the active slot has no usable usage (live for ok or unavailable rows, last-good otherwise), the ordinary sources run and the source label gains " (claude-swap fallback)" or " (tcr fallback)". Inactive slots are never borrowed. The projection now keeps the live measurement time (usageFetchedAt). * Read Claude limits entry by entry for OAuth and Web A malformed limits entry used to fail the whole array on both the OAuth and Web paths, which dropped valid model-scoped weekly rows. Entries are now read field by field: a field of the wrong type reads as missing, a non-object entry is skipped, a non-array limits value reads as empty, and a JSON Boolean is never a percentage. The OAuth response shares the same deserializer, so both paths parse limits identically. Upstream steipete#4614, refs steipete#4126. * Use monthly spend for unified Cursor personal accounts Unified personal Cursor memberships read monthly additional spend from the usage-status endpoint. A valid response supplies spend, cap and reset; an unavailable, malformed or out-of-period response keeps the usage-summary values. Team budgets and other plans are unchanged. Upstream steipete#4593. * Honor the configured OpenCode Go source with a manual cookie A manual cookie used to force the web source for OpenCode Go, so a configured Auto or Local source was ignored. A new provider hook, manual_cookie_keeps_usage_source, lets the shell keep the configured source when the provider offers it. Other providers keep the Web route. Upstream steipete#4616. * Prefer ordinary Gemini quota buckets for the Antigravity summary The Antigravity quota summary now represents Gemini with the ordinary gemini-5h (or session alias) and gemini-weekly buckets, matched by normalized ID rather than label. A model-specific Gemini allowance represents Gemini only when the ordinary bucket of that cadence is absent, unknown or disabled. All buckets stay visible as extra windows. Upstream: CodexBar steipete#4659 (389d287). * Scan larger Antigravity local histories Raise the aggregate local-history scan bounds to 100,000 rows, 512 MiB of attempted payload and 30 seconds. Per-file, per-record and schema bounds and cancellation are unchanged. Adds regression coverage for history beyond the former 50,000-row cap. Upstream: CodexBar steipete#4639 (5818152). * Accept catalog provider IDs in the --provider flag Add a regression test that every catalog ID reported by config providers --format json selects that same provider. The behavior already held on Windows; the test keeps it from regressing (upstream steipete#4643). * Report OpenCode Go local cost history in the cost command codexbar cost --provider opencodego read no local history and reported local cost scanning as unavailable. It now reads recorded OpenCode Go usage for the selected window (upstream steipete#4606). Known gap: unknown-token presentation and the per-model token breakdown are not ported. The shared CostSummary cannot show unknown tokens, so incomplete token sums are shown as zero. * Keep unsupported Pi backends as unpriced standalone history Pi/OMP rows from backends without native pricing are kept in standalone Pi history as unpriced tokens labeled backend/model. They never borrow native Codex or Claude prices. Native Codex and Claude scans skip these rows without losing completeness. Malformed rows from unsupported backends mark only standalone history incomplete. Missing timestamps still keep standalone history incomplete (upstream steipete#4628, fixes steipete#4605). * Retrigger CI --------- Co-authored-by: Factory Agent <agent@factory.dev>
Follow-up to #4126 (comment).
OAuth decoded the whole
limitsarray as one typed value undertry?, so one entry with an unexpected field type dropped every model-scoped weekly row. OAuth now uses the Web path's existing per-entry parser, and the OAuth-only limit types are removed.Tests
ClaudeOAuthTests: a sibling entry with a stringpercentand a Fable entry with a numericresets_at. The Fable row stays at 5% with no reset time. Fails onmain.Runtime proof
Live OAuth run, this branch's CLI and 0.77.0 (identity fields omitted). Both print:
Question
A JSON boolean
percentreads as 1% / 0% on both paths, the same as Web onmain. Intended, or should it be dropped like a string? (Also asked in #4126 (comment).)Commands run
🤖 Generated with Claude Code